/* Copyright (C) 2011  Chris Mueller <ruunhb@googlemail.com>
 *
 * This file is part of the Liquid Programming Language
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <glib/gstring.h>
#include "parser.h"
#include "nodes/core.h"
#include "nodes/structure.h"
#include "tree_generator.h"

struct TreeGenerator {
    GString* buffer;
};

struct TreeViz {
};


struct TreeGenerator*
lq_nodeviz_treegen_new(void)
{
    struct TreeGenerator* treegen = g_new(struct TreeGenerator, 1);
    treegen->buffer = g_string_sized_new(512);

    return treegen;
}

void
lq_nodeviz_treegen_read(struct TreeGenerator* gen, FILE* stream)
{
    g_assert(stream != 0);
    g_string_erase(gen->buffer, 0, -1);
    char hStr[128];

    while(!feof(stream) && (fgets(hStr, 100, stream) != 0)) {
        g_string_append(gen->buffer, hStr);
        memset(hStr, 0, 128);
    }
}

void
lq_nodeviz_treegen_free(struct TreeGenerator* treegen)
{
    g_string_free(treegen->buffer, TRUE);
    g_free(treegen);
}


Agraph_t*
lq_nodeviz_treegen_graph(struct TreeGenerator* gen, const struct TreeViz* vizkit)
{
    struct LqFile* f = lq_file_new(gen->buffer->str, 0);
    struct LqScanner* s = lq_scanner_new(f); 
    struct LqParser* p = lq_parser_new(s);

    GNode* root = lq_parser_construct_ast(p);

    Agraph_t* g = agopen("test", AGRAPH);
    lq_nodeviz_treegen_agnode_new(g, root, vizkit);

    lq_parser_free_ast(root);
    lq_parser_free(p);
    lq_scanner_free(s);
    lq_file_free(f);

    return g;
}


void lq_nodeviz_agnode_label(Agnode_t* n, gchar* title, gchar* name)
{
    unsigned size = strlen(title) + strlen(name) + 2;
    gchar* str = g_new(gchar, size); 
    g_snprintf(str, size, "%s\n%s", title, name);
    agset(n, "label", str);
    agsafeset(n, "shape", "box", "");
    g_free(str);
}


Agnode_t* 
lq_nodeviz_treegen_agnode_new(Agraph_t* g, GNode* node, const struct TreeViz* vizkit)
{
    g_assert(node);

    static int node_index = 0;
    char key[64];
    unsigned size;
    const gchar *name, *space;
    guint i;

    LqNode* info = lq_cast(struct LqNode*, node->data);
    guint childs = g_node_n_children(node);
    
    sprintf(key, "node%d", node_index++);
    Agnode_t* n = agnode(g, key);

    switch(info->kind) {
        case LQ_NODE_ROOT:
            agset(n, "label", "root");
            agsafeset(n, "color", "navy", "");
            agsafeset(n, "fontcolor", "navy", "");
            agsafeset(n, "style", "bold", "");
            break;

        case NODE_OPERATION:
            agset(n, "label", (gchar*) lq_gnode_operation_string(node));
            agsafeset(n, "shape", "hexagon", "");
            agsafeset(n, "margin", "0.001", "");
            break;

        case NODE_INTEGER:
            lq_nodeviz_agnode_label(n, "<Integer>", (gchar*) lq_gnode_literal_string(node));
            break;

        case NODE_FLOATPOINT:            
            lq_nodeviz_agnode_label(n, "<Float>", (gchar*) lq_gnode_literal_string(node));
            break;

        case NODE_CHARACTER:
            lq_nodeviz_agnode_label(n, "<Char>", (gchar*) lq_gnode_literal_string(node));
            break;

        case NODE_STRING:
            lq_nodeviz_agnode_label(n, "<String>", (gchar*) lq_gnode_literal_string(node));
            break;

        case NODE_ATOM:
            agset(n, "label", (gchar*) lq_gnode_literal_string(node));
            agsafeset(n, "shape", "box", "");
            break;

        case NODE_FUN_DECLARATION:
            agset(n, "label", (gchar*) LQ_CAST(LqFunDeclNode, node->data)->name);
            break;

        case NODE_MODULE:
            lq_nodeviz_agnode_label(n, "<Module>", (gchar*) lq_gnode_module_name(node));
            agsafeset(n, "shape", "box3d", "");
            break;

        case NODE_FUNCTION:
            name = LQ_CAST(LqFunCallNode, node->data)->name;
            space = LQ_CAST(LqFunCallNode, node->data)->space;

            agsafeset(n, "shape", "box", "");

            if(space == 0) {
                agset(n, "label", (gchar*) name);
            } else {
                size = strlen(name) + strlen(space) + 4;
                gchar label[size];
                g_snprintf(label, size, "%s\n%s", space, name);
                agset(n, "label", (gchar*) label);
            }
            break;
        default:
            agset(n, "label", (gchar*) lq_gnode_string(node));
            break;
    }

    for(i = 0; i < childs; i++) {
        GNode* child = g_node_nth_child(node, i);
        Agnode_t* child_n = lq_nodeviz_treegen_agnode_new(g, child, vizkit);
        agedge(g, n, child_n);
    }

    return n;
}
